package com.panfeng.xcloud.scheduler.service.impl;

import cn.hutool.core.util.RandomUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.binance.client.model.enums.CandlestickInterval;
import com.panfeng.xcloud.boss.provider.dto.request.OperateBotReqDTO;
import com.panfeng.xcloud.boss.provider.dto.request.PinBarCandlerStickDetailDTO;
import com.panfeng.xcloud.boss.provider.dto.request.QuantBotDetailDTO;
import com.panfeng.xcloud.boss.provider.dto.request.QuantCrossCandlerStickDetailDTO;
import com.panfeng.xcloud.common.core.candle.Entry;
import com.panfeng.xcloud.common.core.candle.MyCandleEntry;
import com.panfeng.xcloud.common.core.configuration.DataDictionaryConfig;
import com.panfeng.xcloud.common.core.enums.*;
import com.panfeng.xcloud.common.core.exceptions.BaseBizException;
import com.panfeng.xcloud.common.core.utils.KeyContactUtil;
import com.panfeng.xcloud.common.core.web.vo.OrderDetailResponseVO;
import com.panfeng.xcloud.common.core.web.vo.TradeResponseVO;
import com.panfeng.xcloud.dao.assets.entity.UserBot;
import com.panfeng.xcloud.dao.assets.entity.UserWalletInfo;
import com.panfeng.xcloud.scheduler.constants.SystemParamContants;
import com.panfeng.xcloud.scheduler.dao.mapper.CoinListMapperExt;
import com.panfeng.xcloud.scheduler.dao.mapper.UserBotMapperExt;
import com.panfeng.xcloud.scheduler.dao.mapper.UserExchangePwdMapperExt;
import com.panfeng.xcloud.scheduler.dao.mapper.UserWalletInfoMapperExt;
import com.panfeng.xcloud.scheduler.service.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.ta4j.core.BarSeries;
import org.ta4j.core.num.Num;

import java.math.BigDecimal;
import java.time.Duration;
import java.util.Iterator;
import java.util.List;

@Slf4j
@Service
public class QuantBotServiceImpl implements IQuantBotService {

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private DataDictionaryConfig dataDictionaryConfig;

    @Autowired
    private UserBotMapperExt userBotMapperExt;

    @Autowired
    private UserWalletInfoMapperExt userWalletInfoMapperExt;

    @Autowired
    private IFeignService feignService;

    @Autowired
    private IUtilService iUtilService;

    @Autowired
    private IRiskManagerService iRiskManagerService;

    @Autowired
    private ICalculatePriceService iCalculatePriceService;

    @Autowired
    private IOffSetService iOffSetService;

    @Autowired
    private ITechniqueSymbolService iTechniqueSymbolService;

    @Value("${xdcloud.des3.secret-key}")
    private String secrectKey;

    @Override
    public void runQuantBot() {
        //找到所有正在运行中的机器人
        List<UserBot> userBots = userBotMapperExt.selectBots(Integer.valueOf(UserBotStatusTypeEnum.ACTIVE.getCode()), null);
        log.info("当前执行机器人数量:{}", userBots.size());
        Iterator<UserBot> iterator = userBots.iterator();
        while (iterator.hasNext()) {
            UserBot userBot = iterator.next();
            String userBotKey = KeyContactUtil.getUserBotKey(userBot.getExchangeType(), userBot.getDirection(), userBot.getLeverRate(), userBot.getDestinationCoin(), userBot.getSourceCoin(), userBot.getUserId());
            String userId = userBot.getUserId();
            String contractCode = KeyContactUtil.getContractCode(userBot.getExchangeType(), userBot.getDestinationCoin(), userBot.getSourceCoin());
            QuantBotDetailDTO quantBotDetailDTO = JSONObject.parseObject(stringRedisTemplate.opsForValue().get(userBotKey), QuantBotDetailDTO.class);

            try {
                if (null != quantBotDetailDTO && quantBotDetailDTO.getStatus().equals(UserBotStatusTypeEnum.ACTIVE.getCode())) {
                    Long t1 = System.currentTimeMillis();
                    robotExecute(quantBotDetailDTO, userBotKey);
                    Long t2 = System.currentTimeMillis();
                    log.info("当前机器人执行用户:{},币对:{},多空方向:{},杠杆倍数:{},该次执行耗时:{}", userId, contractCode, userBot.getDirection(), userBot.getLeverRate(), t2 - t1);
                }
            } catch (Exception e) {
                log.error("用户:{}的机器人执行异常,币对:{},多空方向:{},杠杆倍数:{},异常信息:{}", userId, contractCode, userBot.getDirection(), userBot.getLeverRate(), e.getMessage(), e);

                //需要清除机器人的异常
                needDeleteBotException(userBotKey, quantBotDetailDTO, e);

                //无需发提醒邮件的异常
                boolean noNeedSendEmail = noNeedSendEmailException(e);
                if (!noNeedSendEmail) {
                    feignService.sendEmail("   用户id:" + userId
                            + "   方向:" + userBot.getDirection()
                            + "   倍数:" + userBot.getLeverRate()
                            + "   交易对:" + contractCode
                            + "   异常信息:" + com.panfeng.xcloud.common.core.utils.StringUtils.printStackTraceToString(e));
                }
                continue;
            }
        }
    }

    private void robotExecute(QuantBotDetailDTO quantBotDetailDTO, String userBotKey) throws Exception {
        JSONObject candleStick = feignService.getCandleStick(Integer.valueOf(quantBotDetailDTO.getExchangeType()), quantBotDetailDTO.getUserId(),
                quantBotDetailDTO.getDestinationCoin(), quantBotDetailDTO.getSourceCoin(), CandlestickInterval.FIFTEEN_MINUTES, null, null, 480);
        Duration barSeriesTimePeriod = Duration.ofMinutes(15);
        BigDecimal inteval = new BigDecimal("900000");

        // 定义计算 ATR 的参数
        int atrPeriod = 13;
        //均价为0则走择机开仓逻辑,均价大于0则走已经开仓的正常机器人逻辑
        if (new BigDecimal(quantBotDetailDTO.getPositionAmount()).compareTo(BigDecimal.ZERO) == 0) {
            //判断点卡余额是否足够，不够就停掉机器人
            judgePointBalance(userBotKey, quantBotDetailDTO);
            quantBotDetailDTO = robotOpenPositionExecute(candleStick, quantBotDetailDTO, userBotKey, atrPeriod, barSeriesTimePeriod, inteval);
        } else if (new BigDecimal(quantBotDetailDTO.getPositionAmount()).compareTo(BigDecimal.ZERO) > 0) {
            quantBotDetailDTO = robotRunningExecute(candleStick, quantBotDetailDTO, userBotKey, atrPeriod, barSeriesTimePeriod, inteval);
        } else {
            throw new BaseBizException(ResponseStatusEnum.AVGPRICE_ERROR);
        }

        //更新交易对详情
        toSaveQuantBotDetailDTO(userBotKey, quantBotDetailDTO);
    }

    private QuantBotDetailDTO robotOpenPositionExecute(JSONObject candleStick, QuantBotDetailDTO quantBotDetailDTO, String userBotKey, int atrPeriod, Duration barSeriesTimePeriod, BigDecimal inteval) throws Exception {
        BigDecimal lastAutoClearTime = new BigDecimal(quantBotDetailDTO.getCurrentAutoClearTime());
        BigDecimal nowTime = new BigDecimal(System.currentTimeMillis());

        if (quantBotDetailDTO.getDirection().equals(PositionSideTypeEnum.BUY.getCode())) {
            if ((nowTime.subtract(lastAutoClearTime).compareTo(inteval) >= 0) &&
                    iTechniqueSymbolService.judgeTrendBuyBreakThroughOpen2(candleStick, atrPeriod, quantBotDetailDTO, barSeriesTimePeriod)
            ) {
                quantBotDetailDTO = openPosition(quantBotDetailDTO, userBotKey);
            }
        } else if (quantBotDetailDTO.getDirection().equals(PositionSideTypeEnum.SELL.getCode())) {
            if ((nowTime.subtract(lastAutoClearTime).compareTo(inteval) >= 0) &&
                    iTechniqueSymbolService.judgeTrendSellBreakThroughOpen2(candleStick, atrPeriod, quantBotDetailDTO, barSeriesTimePeriod)
            ) {
                quantBotDetailDTO = openPosition(quantBotDetailDTO, userBotKey);
            }
        } else {
            throw new BaseBizException(ResponseStatusEnum.ROBOT_DIRECTION_NOT_EXIST);
        }

        JSONArray candleSticks = candleStick.getJSONArray("candlesticks");
        JSONObject obj = (JSONObject) (candleSticks.get(candleSticks.size() - 1));
        BigDecimal close = new BigDecimal(obj.getFloatValue("close"));
        quantBotDetailDTO.setCurrentBidPrice(String.valueOf(close));

        return quantBotDetailDTO;
    }

    private QuantBotDetailDTO robotRunningExecute(JSONObject candleStick, QuantBotDetailDTO quantBotDetailDTO, String userBotKey, int atrPeriod, Duration barSeriesTimePeriod, BigDecimal inteval) throws Exception {
        //获取5分钟周期的蜡烛
        List<MyCandleEntry> candleEntries = iTechniqueSymbolService.getCandlerStick(candleStick, quantBotDetailDTO);
        MyCandleEntry nowCandleEntry = candleEntries.get(candleEntries.size() - 1);
        MyCandleEntry lastCandleEntry = candleEntries.get(candleEntries.size() - 2);

        //当前价格
        BigDecimal price = new BigDecimal(nowCandleEntry.getClose());
        //当前涨幅
        BigDecimal rise = null;
        //最高涨幅
        BigDecimal highRise = null;
        //当前均价
        BigDecimal avgPrice = new BigDecimal(quantBotDetailDTO.getAvgPrice());

        BigDecimal compareNowOpenRatio = null;

        //距离开仓时间间隔
        BigDecimal openTime = new BigDecimal(quantBotDetailDTO.getCurrentAddTime());
        BigDecimal nowTime = new BigDecimal(System.currentTimeMillis());

        if (quantBotDetailDTO.getDirection().equals(PositionSideTypeEnum.BUY.getCode())) {
            //如果是多单,那么上涨是止盈,下跌是补仓
            rise = (price.subtract(avgPrice)).divide(avgPrice, 6, BigDecimal.ROUND_DOWN);

            if (nowTime.subtract(openTime).compareTo(inteval) <= 0) {
                highRise = (new BigDecimal(nowCandleEntry.getClose()).subtract(avgPrice)).divide(avgPrice, 6, BigDecimal.ROUND_DOWN);
            } else {
                highRise = (new BigDecimal(nowCandleEntry.getHigh()).subtract(avgPrice)).divide(avgPrice, 6, BigDecimal.ROUND_DOWN);
            }
            compareNowOpenRatio = (new BigDecimal(nowCandleEntry.getOpen()).subtract(avgPrice)).divide(avgPrice, 6, BigDecimal.ROUND_DOWN);
        } else if (quantBotDetailDTO.getDirection().equals(PositionSideTypeEnum.SELL.getCode())) {
            //如果是空单,那么上涨是补仓,下跌是止盈
            rise = (avgPrice.subtract(price)).divide(avgPrice, 6, BigDecimal.ROUND_DOWN);

            if (nowTime.subtract(openTime).compareTo(inteval) <= 0) {
                highRise = (avgPrice.subtract(new BigDecimal(nowCandleEntry.getClose()))).divide(avgPrice, 6, BigDecimal.ROUND_DOWN);
            } else {
                highRise = (avgPrice.subtract(new BigDecimal(nowCandleEntry.getLow()))).divide(avgPrice, 6, BigDecimal.ROUND_DOWN);
            }
            compareNowOpenRatio = (avgPrice.subtract(new BigDecimal(nowCandleEntry.getOpen()))).divide(avgPrice, 6, BigDecimal.ROUND_DOWN);
        } else {
            throw new BaseBizException(ResponseStatusEnum.ROBOT_DIRECTION_NOT_EXIST);
        }

        quantBotDetailDTO.setAskRise(rise.toString());

        log.info("当前持仓金额:{}", quantBotDetailDTO.getPositionAmount());
        log.info("当前持仓数量:{}", quantBotDetailDTO.getPositionNum());
        log.info("当前持仓均价:{}", quantBotDetailDTO.getAvgPrice());
        //log.info("当前买盘价格:{}", bidPrice);
        log.info("当前卖盘价格:{}", price);
        //log.info("当前买盘涨幅:{}%", bidRise.multiply(new BigDecimal("100")));
        log.info("当前卖盘涨幅:{}%", rise.multiply(new BigDecimal("100")));

        //如果当前涨幅>曾有过的最高涨幅，则设置当前涨幅为曾有过的最高涨幅
        if (highRise.compareTo(new BigDecimal(quantBotDetailDTO.getHighRise())) > 0) {
            quantBotDetailDTO.setHighRise(String.valueOf(highRise));
            PinBarCandlerStickDetailDTO pinBarCandlerStickDetailDTO = quantBotDetailDTO.getPinBarCandlerStickDetailDTO();
            /*BigDecimal stopWinRate = pinBarCandlerStickDetailDTO.getStopWinRate();
            if (highRise.compareTo(stopWinRate) >= 0) {
                BigDecimal breakEvenRate = null;
                BarSeries barSeries5m = iTechniqueSymbolService.getBarSeries(candleEntries, barSeriesTimePeriod);
                List<Num> atrs = iTechniqueSymbolService.getATR(barSeries5m, atrPeriod);
                BigDecimal atr = new BigDecimal(atrs.get(atrs.size() - 1).floatValue()).multiply(new BigDecimal("2"));

                if (quantBotDetailDTO.getDirection().equals(PositionSideTypeEnum.BUY.getCode())) {
                    breakEvenRate = (new BigDecimal(nowCandleEntry.getHigh()).subtract(atr)).subtract(avgPrice).divide(avgPrice, 6, BigDecimal.ROUND_DOWN);
                } else if (quantBotDetailDTO.getDirection().equals(PositionSideTypeEnum.SELL.getCode())) {
                    breakEvenRate = (avgPrice).subtract(new BigDecimal(nowCandleEntry.getLow()).add(atr)).divide(avgPrice, 6, BigDecimal.ROUND_DOWN);
                }
                breakEvenRate = breakEvenRate.compareTo(SystemParamContants.fee) > 0 ? breakEvenRate : SystemParamContants.fee;
                pinBarCandlerStickDetailDTO.setBreakEvenRate(breakEvenRate);
            }*/

            if (highRise.compareTo(new BigDecimal(0.11)) > 0) {
                pinBarCandlerStickDetailDTO.setStopWinRate(new BigDecimal(0.11));
                pinBarCandlerStickDetailDTO.setBreakEvenRate(new BigDecimal(0.1));
            } else if (highRise.compareTo(new BigDecimal(0.1)) > 0) {
                pinBarCandlerStickDetailDTO.setStopWinRate(new BigDecimal(0.1));
                pinBarCandlerStickDetailDTO.setBreakEvenRate(new BigDecimal(0.09));
            } else if (highRise.compareTo(new BigDecimal(0.09)) > 0) {
                pinBarCandlerStickDetailDTO.setStopWinRate(new BigDecimal(0.09));
                pinBarCandlerStickDetailDTO.setBreakEvenRate(new BigDecimal(0.08));
            } else if (highRise.compareTo(new BigDecimal(0.08)) > 0) {
                pinBarCandlerStickDetailDTO.setStopWinRate(new BigDecimal(0.08));
                pinBarCandlerStickDetailDTO.setBreakEvenRate(new BigDecimal(0.07));
            } else if (highRise.compareTo(new BigDecimal(0.07)) > 0) {
                pinBarCandlerStickDetailDTO.setStopWinRate(new BigDecimal(0.07));
                pinBarCandlerStickDetailDTO.setBreakEvenRate(new BigDecimal(0.06));
            } else if (highRise.compareTo(new BigDecimal(0.06)) > 0) {
                pinBarCandlerStickDetailDTO.setStopWinRate(new BigDecimal(0.06));
                pinBarCandlerStickDetailDTO.setBreakEvenRate(new BigDecimal(0.05));
            } else if (highRise.compareTo(new BigDecimal(0.05)) > 0) {
                pinBarCandlerStickDetailDTO.setStopWinRate(new BigDecimal(0.05));
                pinBarCandlerStickDetailDTO.setBreakEvenRate(new BigDecimal(0.04));
            } else if (highRise.compareTo(new BigDecimal(0.04)) > 0) {
                pinBarCandlerStickDetailDTO.setStopWinRate(new BigDecimal(0.04));
                pinBarCandlerStickDetailDTO.setBreakEvenRate(new BigDecimal(0.03));
            } else if (highRise.compareTo(new BigDecimal(0.03)) > 0) {
                pinBarCandlerStickDetailDTO.setStopWinRate(new BigDecimal(0.03));
                pinBarCandlerStickDetailDTO.setBreakEvenRate(new BigDecimal(0.02));
            } else if (highRise.compareTo(new BigDecimal(0.02)) > 0) {
                pinBarCandlerStickDetailDTO.setStopWinRate(new BigDecimal(0.02));
                pinBarCandlerStickDetailDTO.setBreakEvenRate(new BigDecimal(0.01));
            }

            quantBotDetailDTO.setPinBarCandlerStickDetailDTO(pinBarCandlerStickDetailDTO);
        }

        if (quantBotDetailDTO.getDirection().equals(PositionSideTypeEnum.BUY.getCode())) {
            if (new BigDecimal(quantBotDetailDTO.getHighRise()).compareTo(quantBotDetailDTO.getPinBarCandlerStickDetailDTO().getStopWinRate()) >= 0
                    && (rise.compareTo(quantBotDetailDTO.getPinBarCandlerStickDetailDTO().getBreakEvenRate()) < 0)) {
                quantBotDetailDTO = toExecuteAntiRise(quantBotDetailDTO, userBotKey, SystemInnerTypeEnum.ACHIEVE_BREAK_EVEN_CLEAR.code);
                quantBotDetailDTO.setCurrentAutoClearTime(String.valueOf(System.currentTimeMillis()));
            } else if (price.compareTo(quantBotDetailDTO.getPinBarCandlerStickDetailDTO().getInitStopLossPrice()) < 0) {
                quantBotDetailDTO = toExecuteAntiRise(quantBotDetailDTO, userBotKey, SystemInnerTypeEnum.STOP_LOSS_INIT_AUTO_CLEAR.code);
                quantBotDetailDTO.setCurrentAutoClearTime(String.valueOf(System.currentTimeMillis()));
            } else if (rise.compareTo(SystemParamContants.stop_loss_max.negate()) <= 0) {
                quantBotDetailDTO = toExecuteAntiRise(quantBotDetailDTO, userBotKey, SystemInnerTypeEnum.MAX_STOP_LOSS_CLEAR.code);
                quantBotDetailDTO.setCurrentAutoClearTime(String.valueOf(System.currentTimeMillis()));
            }
        } else if (quantBotDetailDTO.getDirection().equals(PositionSideTypeEnum.SELL.getCode())) {
            if (new BigDecimal(quantBotDetailDTO.getHighRise()).compareTo(quantBotDetailDTO.getPinBarCandlerStickDetailDTO().getStopWinRate()) >= 0
                    && (rise.compareTo(quantBotDetailDTO.getPinBarCandlerStickDetailDTO().getBreakEvenRate()) < 0)) {
                quantBotDetailDTO = toExecuteAntiRise(quantBotDetailDTO, userBotKey, SystemInnerTypeEnum.ACHIEVE_BREAK_EVEN_CLEAR.code);
                quantBotDetailDTO.setCurrentAutoClearTime(String.valueOf(System.currentTimeMillis()));
            } else if (price.compareTo(quantBotDetailDTO.getPinBarCandlerStickDetailDTO().getInitStopLossPrice()) > 0) {
                quantBotDetailDTO = toExecuteAntiRise(quantBotDetailDTO, userBotKey, SystemInnerTypeEnum.STOP_LOSS_INIT_AUTO_CLEAR.code);
                quantBotDetailDTO.setCurrentAutoClearTime(String.valueOf(System.currentTimeMillis()));
            } else if (rise.compareTo(SystemParamContants.stop_loss_max.negate()) <= 0) {
                quantBotDetailDTO = toExecuteAntiRise(quantBotDetailDTO, userBotKey, SystemInnerTypeEnum.MAX_STOP_LOSS_CLEAR.code);
                quantBotDetailDTO.setCurrentAutoClearTime(String.valueOf(System.currentTimeMillis()));
            }
        } else {
            throw new BaseBizException(ResponseStatusEnum.ROBOT_DIRECTION_NOT_EXIST);
        }

        //更新上次市价
        quantBotDetailDTO.setCurrentBidPrice(String.valueOf(price));

        return quantBotDetailDTO;
    }

    public void chooseClear(QuantBotDetailDTO quantBotDetailDTO, Integer systemInnerType) throws Exception {
        OperateBotReqDTO operateBotReqDTO = initOperateBotReqDTO(OperateTypeEnum.SELL.getCode(), quantBotDetailDTO);
        //这里就去下单
        feignService.chooseSendOrderHttp(operateBotReqDTO, systemInnerType);
        log.info("执行了清仓");
    }

    private OrderDetailResponseVO chooseDouble(QuantBotDetailDTO quantBotDetailDTO, BigDecimal amount, Integer systemInnerType) throws Exception {
        OperateBotReqDTO operateBotReqDTO = initOperateBotReqDTO(OperateTypeEnum.BUY.getCode(), quantBotDetailDTO);

        BigDecimal avaliablePieces = feignService.getAvaliablePieces(Integer.valueOf(operateBotReqDTO.getExchangeType()), operateBotReqDTO);

        //判断该交易对可下单数是否足够
        if (avaliablePieces.compareTo(amount) < 0) {
            throw new BaseBizException(ResponseStatusEnum.AVAILABLE_OPEN_AMOUNT_NOT_ENOUGH);
        }

        //获取真实下单数
        BigDecimal realAmount = feignService.toSetOpenOrderAmount(operateBotReqDTO, amount);
        operateBotReqDTO.setAmount(realAmount);

        TradeResponseVO tradeResponseVO = feignService.chooseSendOrderHttp(operateBotReqDTO, systemInnerType);
        OrderDetailResponseVO orderDetail = feignService.getOrderDetail(Integer.valueOf(operateBotReqDTO.getExchangeType()), operateBotReqDTO, tradeResponseVO);
        return orderDetail;
    }

    private OperateBotReqDTO initOperateBotReqDTO(int operateType, QuantBotDetailDTO quantBotDetailDTO) {
        OperateBotReqDTO operateBotReqDTO = new OperateBotReqDTO();
        operateBotReqDTO.setUserId(quantBotDetailDTO.getUserId());
        operateBotReqDTO.setOperateType(operateType);
        operateBotReqDTO.setSourceCoin(quantBotDetailDTO.getSourceCoin());
        operateBotReqDTO.setDestinationCoin(quantBotDetailDTO.getDestinationCoin());
        operateBotReqDTO.setExchangeType(Integer.valueOf(quantBotDetailDTO.getExchangeType()));
        operateBotReqDTO.setAmount(new BigDecimal(quantBotDetailDTO.getPositionPiece()));
        operateBotReqDTO.setDirection(quantBotDetailDTO.getDirection());
        operateBotReqDTO.setLeverRate(Integer.valueOf(quantBotDetailDTO.getLeverRate()));
        return operateBotReqDTO;
    }

    private QuantBotDetailDTO judgeStrategy(QuantBotDetailDTO quantBotDetailDTO, String userBotKey) throws Exception {
        //判断点卡余额是否足够，不够就停掉机器人
        judgePointBalance(userBotKey, quantBotDetailDTO);

        //清仓后需判断是单次策略还是循环策略，如果是循环策略则直接开单，更新redis;如果是单次策略则直接更新redis并且clear机器人
        if (quantBotDetailDTO.getStrategyType().equals(TradeStrategyTypeEnum.CIRCLE.getCode())) {
            quantBotDetailDTO.setHighRise("0");
            quantBotDetailDTO.setBidRise("0");
            quantBotDetailDTO.setAskRise("0");
            quantBotDetailDTO.setCurrentAddCount("0");
            quantBotDetailDTO.setCurrentAntiAddCount("0");
            //更新当前持仓数量(币计)
            quantBotDetailDTO.setPositionNum("0");
            //更新当前持仓数量(不确定是什么单位,该交易所下单需要什么单位就是什么单位)
            quantBotDetailDTO.setPositionPiece("0");
            //更新持仓金额
            quantBotDetailDTO.setPositionAmount("0");
            //更新持仓均价
            quantBotDetailDTO.setAvgPrice("0");
            //开仓时间清零
            quantBotDetailDTO.setCurrentAddTime("0");
            //是否达到过设定止盈涨幅初始化
            quantBotDetailDTO.setAchieveStopWin(false);
            //是否达到过保本涨幅1初始化
            quantBotDetailDTO.setAchieveBreakEven1(false);
            //是否达到过保本涨幅2初始化
            quantBotDetailDTO.setAchieveBreakEven2(false);
            quantBotDetailDTO.setDivergence(false);
            //交叉时间清零
            quantBotDetailDTO.setCrossTime("0");
            //交叉蜡烛对象清空
            quantBotDetailDTO.setQuantCrossCandlerStickDetailDTO(new QuantCrossCandlerStickDetailDTO());
            //pinBar信号时间清零
            quantBotDetailDTO.setPinbarTime("0");
            //pinBar对象清空
            quantBotDetailDTO.setPinBarCandlerStickDetailDTO(new PinBarCandlerStickDetailDTO());
        } else {
            //如果是单次策略,清除redis中的机器人
            iUtilService.deleteBot(userBotKey, quantBotDetailDTO);

            log.info("单次策略，机器人结束!");
            throw new BaseBizException(ResponseStatusEnum.SINGLE_STRATEGY_ERROR);
        }
        return quantBotDetailDTO;
    }

    private QuantBotDetailDTO toExecuteAntiRise(QuantBotDetailDTO quantBotDetailDTO, String userBotKey, Integer SystemInnerTypeEnumCode) throws Exception {
        //调用清仓方法
        chooseClear(quantBotDetailDTO, SystemInnerTypeEnumCode);

        //清仓后需判断是单次策略还是循环策略，如果是循环策略则直接开单，更新redis；如果是单次策略则直接更新redis并且clear机器人
        quantBotDetailDTO = judgeStrategy(quantBotDetailDTO, userBotKey);

        return quantBotDetailDTO;
    }

    private QuantBotDetailDTO toExecuteAntiDown(QuantBotDetailDTO quantBotDetailDTO, String userBotKey) throws Exception {
        if (quantBotDetailDTO.getLock()) {
            log.info("当前正处于锁仓状态，不再执行逆向加仓");
            return quantBotDetailDTO;
        }
        int currentAntiAddCount = Integer.valueOf(quantBotDetailDTO.getCurrentAntiAddCount());
        int antiAddCount = Integer.valueOf(quantBotDetailDTO.getAntiAddCount());
        if (currentAntiAddCount >= antiAddCount) {
            log.info("[当前逆向加仓次数]到达[设定的逆向加仓次数极限],不再执行加仓");
            return quantBotDetailDTO;
        }

        //该次逆向加仓金额
        BigDecimal amount = iCalculatePriceService.getAntiNeedAskPieces(quantBotDetailDTO);

        //逆向加仓次数加1
        currentAntiAddCount++;

        log.info("开始逆向加仓,本次加仓金额:{}", amount);

        //调用倍投方法,从交易所得到真实增加的持仓数量-------------------------------------------------------------------------
        OrderDetailResponseVO orderDetailResponseVO = chooseDouble(quantBotDetailDTO, amount, SystemInnerTypeEnum.ANTI_ADD_OPEN.code);
        //更新当前持仓金额
        quantBotDetailDTO.setPositionAmount(new BigDecimal(quantBotDetailDTO.getPositionAmount()).add(orderDetailResponseVO.getTradeTurnover()).toString());
        //更新当前持仓数量(币计)
        quantBotDetailDTO.setPositionNum(new BigDecimal(quantBotDetailDTO.getPositionNum()).add(orderDetailResponseVO.getTradeVolumeNum()).toString());
        //更新当前持仓数量(不确定是什么单位,该交易所下单需要什么单位就是什么单位)
        quantBotDetailDTO.setPositionPiece(new BigDecimal(quantBotDetailDTO.getPositionPiece()).add(orderDetailResponseVO.getTradeVolume()).toString());
        //更新持仓均价
        quantBotDetailDTO.setAvgPrice(new BigDecimal(quantBotDetailDTO.getPositionAmount()).divide(new BigDecimal(quantBotDetailDTO.getPositionNum()), 6, BigDecimal.ROUND_DOWN).toString());
        //更新逆向加仓次数
        quantBotDetailDTO.setCurrentAntiAddCount(String.valueOf(currentAntiAddCount));

        log.info("交易对:{},当前持仓数量:{}", userBotKey, quantBotDetailDTO.getPositionNum());

        return quantBotDetailDTO;
    }

    private QuantBotDetailDTO openPosition(QuantBotDetailDTO quantBotDetailDTO, String userBotKey) throws Exception {
        //开仓金额
        BigDecimal amount = new BigDecimal(quantBotDetailDTO.getInitOrderPosition());

        log.info("开仓,本次开仓金额:{}", amount);

        //开仓下单,从交易所得到真实增加的持仓数量-------------------------------------------------------------------------
        OrderDetailResponseVO orderDetailResponseVO = chooseDouble(quantBotDetailDTO, amount, SystemInnerTypeEnum.OPEN_POSITION.code);
        //更新当前持仓金额
        quantBotDetailDTO.setPositionAmount(new BigDecimal(quantBotDetailDTO.getPositionAmount()).add(orderDetailResponseVO.getTradeTurnover()).toString());
        //更新当前持仓数量(币计)
        quantBotDetailDTO.setPositionNum(new BigDecimal(quantBotDetailDTO.getPositionNum()).add(orderDetailResponseVO.getTradeVolumeNum()).toString());
        //更新当前持仓数量(不确定是什么单位,该交易所下单需要什么单位就是什么单位)
        quantBotDetailDTO.setPositionPiece(new BigDecimal(quantBotDetailDTO.getPositionPiece()).add(orderDetailResponseVO.getTradeVolume()).toString());
        //更新持仓均价
        quantBotDetailDTO.setAvgPrice(new BigDecimal(quantBotDetailDTO.getPositionAmount()).divide(new BigDecimal(quantBotDetailDTO.getPositionNum()), 6, BigDecimal.ROUND_DOWN).toString());
        //更新开仓时间
        quantBotDetailDTO.setCurrentAddTime(String.valueOf(System.currentTimeMillis()));
        //交叉信号时间归零
        quantBotDetailDTO.setCrossTime("0");
        //pinbar信号时间归零
        quantBotDetailDTO.setPinbarTime("0");

        log.info("交易对:{},当前持仓数量:{}", userBotKey, quantBotDetailDTO.getPositionNum());

        return quantBotDetailDTO;
    }

    private BigDecimal getAntiAddPositionRatio(QuantBotDetailDTO quantBotDetailDTO) {
        BigDecimal bigDecimal;
        int currentAntiAddCount = Integer.valueOf(quantBotDetailDTO.getCurrentAntiAddCount());
        switch (currentAntiAddCount) {
            case 0:
                bigDecimal = new BigDecimal(quantBotDetailDTO.getAntiAddPositionRatio1());
                break;
            case 1:
                bigDecimal = new BigDecimal(quantBotDetailDTO.getAntiAddPositionRatio2());
                break;
            case 2:
                bigDecimal = new BigDecimal(quantBotDetailDTO.getAntiAddPositionRatio3());
                break;
            case 3:
                bigDecimal = new BigDecimal(quantBotDetailDTO.getAntiAddPositionRatio4());
                break;
            case 4:
                bigDecimal = new BigDecimal(quantBotDetailDTO.getAntiAddPositionRatio5());
                break;
            case 5:
                bigDecimal = new BigDecimal(quantBotDetailDTO.getAntiAddPositionRatio6());
                break;
            case 6:
                bigDecimal = new BigDecimal(quantBotDetailDTO.getAntiAddPositionRatio7());
                break;
            case 7:
                bigDecimal = new BigDecimal(quantBotDetailDTO.getAntiAddPositionRatio8());
                break;
            case 8:
                bigDecimal = new BigDecimal(quantBotDetailDTO.getAntiAddPositionRatio9());
                break;
            case 9:
                bigDecimal = new BigDecimal(quantBotDetailDTO.getAntiAddPositionRatio10());
                break;
            default:
                bigDecimal = new BigDecimal(quantBotDetailDTO.getAntiAddPositionRatio10());
        }
        return bigDecimal;
    }

    private BigDecimal getQuickClearRatio(QuantBotDetailDTO quantBotDetailDTO) {
        BigDecimal bigDecimal;
        int currentAntiAddCount = Integer.valueOf(quantBotDetailDTO.getCurrentAntiAddCount());
        switch (currentAntiAddCount) {
            case 1:
                bigDecimal = new BigDecimal(quantBotDetailDTO.getAntiAddPositionRatio1()).multiply(new BigDecimal("0"));
                break;
            case 2:
                bigDecimal = new BigDecimal(quantBotDetailDTO.getAntiAddPositionRatio2()).multiply(new BigDecimal("0"));
                break;
            case 3:
                bigDecimal = new BigDecimal(quantBotDetailDTO.getAntiAddPositionRatio3()).multiply(new BigDecimal("0"));
                break;
            case 4:
                bigDecimal = new BigDecimal(quantBotDetailDTO.getAntiAddPositionRatio4()).multiply(new BigDecimal("0"));
                break;
            case 5:
                bigDecimal = new BigDecimal(quantBotDetailDTO.getAntiAddPositionRatio5()).multiply(new BigDecimal("0"));
                break;
            case 6:
                bigDecimal = new BigDecimal(quantBotDetailDTO.getAntiAddPositionRatio6()).multiply(new BigDecimal("0"));
                break;
            case 7:
                bigDecimal = new BigDecimal(quantBotDetailDTO.getAntiAddPositionRatio7()).multiply(new BigDecimal("0"));
                break;
            case 8:
                bigDecimal = new BigDecimal(quantBotDetailDTO.getAntiAddPositionRatio8()).multiply(new BigDecimal("0"));
                break;
            case 9:
                bigDecimal = new BigDecimal(quantBotDetailDTO.getAntiAddPositionRatio9()).multiply(new BigDecimal("0"));
                break;
            case 10:
                bigDecimal = new BigDecimal(quantBotDetailDTO.getAntiAddPositionRatio10()).multiply(new BigDecimal("0"));
                break;
            default:
                bigDecimal = new BigDecimal("0.06").negate();
        }
        return bigDecimal;
    }

    private void needDeleteBotException(String userBotKey, QuantBotDetailDTO quantBotDetailDTO, Exception e) {
        if (e != null && StringUtils.isNotEmpty(e.getMessage())) {
            if (e.getMessage().contains(ErrorCodeTypeEnum.INSUFFICIENT_CLOSE_AMOUNT_AVAILABLE.getCode())
                    || e.getMessage().contains(ErrorCodeTypeEnum.REDUCE_ONLY_REJECTED.getMessage())) {
                //清除机器人
                iUtilService.deleteBot(userBotKey, quantBotDetailDTO);
            }
        }
    }

    private boolean noNeedSendEmailException(Exception e) {
        if (e != null && StringUtils.isNotEmpty(e.getMessage())) {
            boolean e1 = e.getMessage().contains(ResponseStatusEnum.MARKET_PRICE_REQUEST_ERROR.getMessage()) ? true : false;
            return e1;
        }
        return false;
    }

    private void toCheckRealPosition(String userBotKey, QuantBotDetailDTO quantBotDetailDTO, BigDecimal avaliableClosePosition, BigDecimal positionPiece) throws Exception {
        String symbol = RandomUtil.randomNumbers(5);
        feignService.sendEmail("   用户id:" + quantBotDetailDTO.getUserId()
                + "   方向:" + quantBotDetailDTO.getDirection()
                + "   倍数:" + quantBotDetailDTO.getLeverRate()
                + "   交易对:" + quantBotDetailDTO.getContractCode()
                + "   标志数:" + symbol
                + "   异常信息:" + SystemInnerTypeEnum.POSITION_AMOUNT_NOT_EQUAL.info
                + "   交易所实际持仓额:" + avaliableClosePosition
                + "   服务记录持仓额:" + positionPiece);
        /*try {
            quantBotDetailDTO.setPositionPiece(String.valueOf(avaliableClosePosition));
            chooseClear(quantBotDetailDTO, SystemInnerTypeEnum.POSITION_AMOUNT_NOT_EQUAL.code);
        } catch (Exception e) {
            feignService.sendEmail("   用户id:" + quantBotDetailDTO.getUserId()
                    + "   方向:" + quantBotDetailDTO.getDirection()
                    + "   倍数:" + quantBotDetailDTO.getLeverRate()
                    + "   交易对:" + quantBotDetailDTO.getContractCode()
                    + "   标志数:" + symbol
                    + "   指示信息:解决交易所实际持仓和服务记录的持仓仓位不一致时调用清仓报的错"
                    + "   异常信息:" + com.panfeng.xcloud.common.core.utils.StringUtils.printStackTraceToString(e)
                    + "   交易所实际持仓额:" + avaliableClosePosition
                    + "   服务记录持仓额:" + positionPiece);
        }
        quantBotDetailDTO.setPositionPiece(String.valueOf(positionPiece));
        quantBotDetailDTO = judgeStrategy(quantBotDetailDTO, userBotKey);

        //当前价格
        JSONObject smallCandleStick = feignService.getCandleStick(Integer.valueOf(quantBotDetailDTO.getExchangeType()), quantBotDetailDTO.getUserId(),
                quantBotDetailDTO.getDestinationCoin(), quantBotDetailDTO.getSourceCoin(), CandlestickInterval.ONE_MINUTE, null, null, 480);
        List<MyCandleEntry> smallCandleEntries = iTechniqueSymbolService.getCandlerStick(smallCandleStick, quantBotDetailDTO);
        MyCandleEntry smallNowCandleEntry = smallCandleEntries.get(CandlerStickIndexEnum.NOW.code);
        BigDecimal price = new BigDecimal(smallNowCandleEntry.getClose());

        //更新上次市价
        quantBotDetailDTO.setCurrentBidPrice(String.valueOf(price));

        //更新交易对详情
        toSaveQuantBotDetailDTO(userBotKey, quantBotDetailDTO);*/
    }

    private void toSaveQuantBotDetailDTO(String userBotKey, QuantBotDetailDTO newQuantBotDetailDTO) {
        //更新交易对详情
        stringRedisTemplate.opsForValue().set(userBotKey, JSON.toJSONString(newQuantBotDetailDTO));
        QuantBotDetailDTO savedDetailDTO = JSONObject.parseObject(stringRedisTemplate.opsForValue().get(userBotKey), QuantBotDetailDTO.class);

        while (new BigDecimal(savedDetailDTO.getPositionPiece()).compareTo(new BigDecimal(newQuantBotDetailDTO.getPositionPiece())) != 0) {
            feignService.sendEmail("   用户id:" + newQuantBotDetailDTO.getUserId()
                    + "   方向:" + newQuantBotDetailDTO.getDirection()
                    + "   倍数:" + newQuantBotDetailDTO.getLeverRate()
                    + "   交易对:" + newQuantBotDetailDTO.getContractCode()
                    + "   异常信息: 设置机器人对象进redis失败"
                    + "   原对象记录持仓额:" + savedDetailDTO.getPositionPiece()
                    + "   新对象记录持仓额:" + newQuantBotDetailDTO.getPositionPiece());
            stringRedisTemplate.opsForValue().set(userBotKey, JSON.toJSONString(newQuantBotDetailDTO));
            savedDetailDTO = JSONObject.parseObject(stringRedisTemplate.opsForValue().get(userBotKey), QuantBotDetailDTO.class);
        }
    }

    private void judgePointBalance(String userBotKey, QuantBotDetailDTO quantBotDetailDTO) {
        UserWalletInfo userWalletInfo = new UserWalletInfo();
        userWalletInfo.setUserId(quantBotDetailDTO.getUserId());
        userWalletInfo.setChainName(ChainTypeEnum.TRON.getCode());
        userWalletInfo.setCoinName(CoinTypeEnum.USDT.getCode());
        UserWalletInfo walletInfo = userWalletInfoMapperExt.selectOne(userWalletInfo);
        BigDecimal pointMin = new BigDecimal(dataDictionaryConfig.getCodeToNameMap().get(
                DataDictionaryTypeEnum.start_robot_point_balance_min.getCode()));

        //判断点卡余额是否足够
        if (walletInfo.getPointBalance().compareTo(pointMin) < 0) {
            iUtilService.deleteBot(userBotKey, quantBotDetailDTO);
            log.info(quantBotDetailDTO.getUserId() + "点卡余额不足，" + userBotKey + "机器人结束!");
            throw new BaseBizException(ResponseStatusEnum.WALLET_BALANCE_NOT_ENOUGH);
        }
    }

}
